package com.stox.downloader.bse;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.net.URL;
import java.net.URLConnection;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.zip.ZipInputStream;

import com.stox.core.model.Bar;
import com.stox.core.model.Scrip;
import com.stox.core.util.PropertiesUtil;
import com.stox.core.util.StringUtil;
import com.stox.downloader.AbstractDataProvider;
import com.stox.downloader.DataStream;
import com.stox.downloader.DataStreamParser;
import com.stox.ioc.Container;

public class BseDataProvider extends AbstractDataProvider {

	private PropertiesUtil propertiesUtil;
	private BseDataStreamParser bseDataStreamParser;
	private String downloadUrlBseStart = "http://www.bseindia.com/bhavcopy/eq";
	private String downloadUrlBseEnd = "_csv.zip";
	private String downloadUrlBseDateFormat = "ddMMyy";
	private String downloadUrlBseScripFetch = "http://www.bseindia.com/SiteCache/90D/GetQuoteData.aspx?Type=EQ&text=";
	
	public BseDataProvider() {
		this.propertiesUtil = Container.get(PropertiesUtil.class);
		this.bseDataStreamParser = Container.get(BseDataStreamParser.class);
	}
	
	public void afterPropertiesSet(){
		this.downloadUrlBseStart = propertiesUtil.get("download.url.bse.start");
		this.downloadUrlBseEnd = propertiesUtil.get("download.url.bse.end");
		this.downloadUrlBseDateFormat = propertiesUtil.get("download.url.bse.date.format");
		this.downloadUrlBseScripFetch = propertiesUtil.get("download.url.bse.scrip.fetch");
	}
	
	@Override
	public List<Scrip> getScrips() {
		List<String> scripCodes = getScripCodeList();
		final List<Scrip> scrips = new ArrayList<Scrip>(4000);
		if(!scripCodes.isEmpty()){
			ExecutorService executor = Executors.newFixedThreadPool(100);
		
			 for(final String scripCode : scripCodes){
				 executor.execute(new Runnable(){
					 @Override
					public void run() {
						 try{
							 Scrip scrip = getScripDetails(scripCode);
							 if(scrip != null){
								 scrips.add(scrip);
							 }
						 }catch(Exception e){}
					}
				 });
			 }
			 executor.shutdown();
			while (!executor.isTerminated()) {
				Thread.yield();
			}
		}
		Collections.sort(scrips);
		return scrips;
	}
	
	public Date getLastTradingDate(){
		Calendar calendar = null;
		try{
			calendar = Calendar.getInstance();
		}catch(Throwable t){
			t.printStackTrace();
		}
		
		while(true){
			 String data = downloadRawData(constructBreadthUri(calendar.getTime()));
			 if(StringUtil.hasText(data)){
				 return calendar.getTime();
			 }
			 calendar.add(Calendar.DATE, -1);
		}
	}
	
	public List<String> getScripCodeList(){
		List<String> scripCodes = new ArrayList<String>(4000);
		Calendar calendar = null;
		try{
			calendar = Calendar.getInstance();
		}catch(Throwable t){
			t.printStackTrace();
		}
		calendar.add(Calendar.WEEK_OF_YEAR, -1);
		Date today = new Date();
		while(calendar.getTime().before(today)){
			try{
				String downloadUri = constructBreadthUri(calendar.getTime());
				calendar.add(Calendar.DATE, 1);
				String data = downloadRawData(downloadUri);
				DataStream dataStream = getParser().parse(data);
				if(dataStream != null && dataStream.getBarList() != null 
						&& !dataStream.getBarList().isEmpty()){
					for(Bar bar : dataStream.getBarList()){
						if(!scripCodes.contains(bar.getScripCode())){
							scripCodes.add(bar.getScripCode());
						}
					}
				}
			}catch(Exception e){}
		}
		return scripCodes;
	}
	
	public Scrip getScripDetails(String code){
		try{
			URLConnection urlConnection = new URL(downloadUrlBseScripFetch+code).openConnection();
			 urlConnection.setConnectTimeout(30*1000);
			 urlConnection.setReadTimeout(30*1000);
			 InputStream inputStream = urlConnection.getInputStream();
			 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
			 String line = null;
			 StringBuffer stringBuffer = new StringBuffer();
			 while((line = bufferedReader.readLine()) != null){
				 stringBuffer.append(line);
			 }
			return constructScrip(stringBuffer.toString(), code);
		}catch(Exception e){}
		return null; 
	}
	
	public Scrip constructScrip(String s, String code){
		s = s.substring(s.indexOf("<span class='leftspan'>")+"<span class='leftspan'>".length());
		String name = s.substring(0,s.indexOf("</span>"));
		s = s.substring(s.indexOf("<span class='rightspan'>")+"<span class='rightspan'>".length());
		String id = s.substring(0,s.indexOf("</span>"));
		 Scrip scrip = new Scrip();
		 scrip.setCode(code);
		 scrip.setExchangeCode("BO");
		 scrip.setId(id);
		 scrip.setName(name);
		 return scrip;
	}
	
	@Override
	public String getName() {
		return "Bombay Stock Exchange";
	}

	@Override
	public int getOrder() {
		return 0;
	}

	@Override
	public String constructBreadthUri(Date date) {
		DateFormat dateFormat = new SimpleDateFormat(downloadUrlBseDateFormat);
		String uri = downloadUrlBseStart + dateFormat.format(date) + downloadUrlBseEnd;
		return 	uri;
	}
	
	@Override
	public String downloadRawData(String uri) {
		String output = null;
		try{
			ZipInputStream inStream = new ZipInputStream(new URL(uri).openStream());
	        byte[] buffer = new byte[1024];
	        int nrBytesRead;
	        StringWriter w = new StringWriter();
	        if (inStream.getNextEntry() != null) {
	            while ((nrBytesRead = inStream.read(buffer)) > 0) {
	            	w.write(new String(buffer,0,nrBytesRead));
	            }
	        }
	        w.flush();
	        w.close();
	        inStream.close();
			output = w.toString();
		}catch(IOException ie){
			output = "";
		}
		return output;
	}
	
	@Override
	public DataStreamParser getParser() {
		return bseDataStreamParser;
	}
}
